package io.gitee.zhangbinhub.acp.cloud.component

import com.fasterxml.jackson.core.JsonProcessingException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.cloud.conf.AcpCloudLogServerClientConfiguration
import io.gitee.zhangbinhub.acp.cloud.constant.AcpCloudConstant
import io.gitee.zhangbinhub.acp.cloud.constant.AcpCloudLogConstant
import io.gitee.zhangbinhub.acp.cloud.log.AcpCloudLogEvent
import io.gitee.zhangbinhub.acp.cloud.log.AcpCloudLogInfo
import io.gitee.zhangbinhub.acp.cloud.log.AcpCloudLogLevel
import io.gitee.zhangbinhub.acp.cloud.tools.CloudTools
import io.gitee.zhangbinhub.acp.core.common.CalendarTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.acp.core.common.log.LogFactory
import io.gitee.zhangbinhub.acp.core.common.task.BaseAsyncTask
import io.gitee.zhangbinhub.acp.core.common.task.threadpool.ThreadPoolService
import org.apache.commons.lang3.exception.ExceptionUtils
import org.noear.snack4.ONode
import org.noear.solon.Solon
import org.noear.solon.cloud.CloudClient
import org.slf4j.MDC

/**
 * 日志实例
 *
 * @since JDK 17
 */
class CloudLogAdapter(
    private val cloudTools: CloudTools,
    private val acpCloudLogServerClientConfiguration: AcpCloudLogServerClientConfiguration
) : LogAdapter {
    private val stackIndex = 4
    private fun logInstance(): LogFactory =
        LogFactory.getInstance(Thread.currentThread().stackTrace[stackIndex - 1].className, stackIndex)

    private fun generateLogInfo(logLevel: String?, message: String?, t: Throwable? = null): AcpCloudLogInfo =
        AcpCloudLogInfo().apply {
            var logType = acpCloudLogServerClientConfiguration.logType
            if (CommonTools.isNullStr(logType)) {
                logType = AcpCloudLogConstant.DEFAULT_TYPE
            }
            this.logType = logType
            this.serverName = Solon.cfg().appName()
            this.serverIp = cloudTools.getServerIp()
            this.serverPort = cloudTools.getServerPort()
            this.traceId = MDC.get(AcpCloudConstant.MDC_TRACE_ID_KEY)
            this.spanId = MDC.get(AcpCloudConstant.MDC_SPAN_ID_KEY)
            this.logLevel = logLevel
            this.message = message
            this.stackTrace = ExceptionUtils.getStackTrace(t)
        }

    private fun sendToLogServer(acpCloudLogInfo: AcpCloudLogInfo) {
        if (acpCloudLogServerClientConfiguration.enabled) {
            val stacks = Thread.currentThread().stackTrace
            // 启动一个线程池，池中仅有一个线程，保证每个日志消息顺序处理
            val threadPoolService = ThreadPoolService.getInstance(1, 1, Int.MAX_VALUE, "cloud_log_adapter_thread_pool")
            threadPoolService.addTask(object : BaseAsyncTask("cloud_log_adapter_thread_task", false) {
                override fun beforeExecuteFun(): Boolean = true
                override fun afterExecuteFun(result: Any) {}
                override fun executeFun(): Any {
                    acpCloudLogInfo.serverTime = CalendarTools.getNowDateTime().toDate().time
                    acpCloudLogInfo.lineNo = stacks[stackIndex - 1].lineNumber
                    acpCloudLogInfo.className = stacks[stackIndex - 1].className
                    try {
                        CloudClient.event().publish(AcpCloudLogEvent(ONode.serialize(acpCloudLogInfo)))
                    } catch (e: JsonProcessingException) {
                        logInstance().error(e.message, e)
                    }
                    return true
                }
            })
        }
    }

    override fun info(message: String?) {
        logInstance().let { log ->
            log.info(message)
            if (log.isInfoEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Info.name, message))
            }
        }
    }

    override fun info(message: String?, vararg variable: Any?) {
        logInstance().let { log ->
            log.info(message, *variable)
            if (log.isInfoEnabled()) {
                generateLogInfo(AcpCloudLogLevel.Info.name, message).let {
                    it.params = arrayListOf(*variable)
                    sendToLogServer(it)
                }
            }
        }
    }

    override fun info(message: String?, t: Throwable?) {
        logInstance().let { log ->
            log.info(message, t)
            if (log.isInfoEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Info.name, message, t))
            }
        }
    }

    override fun debug(message: String?) {
        logInstance().let { log ->
            log.debug(message)
            if (log.isDebugEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Debug.name, message))
            }
        }
    }

    override fun debug(message: String?, vararg variable: Any?) {
        logInstance().let { log ->
            log.debug(message, *variable)
            if (log.isDebugEnabled()) {
                generateLogInfo(AcpCloudLogLevel.Debug.name, message).let {
                    it.params = arrayListOf(*variable)
                    sendToLogServer(it)
                }
            }
        }
    }

    override fun debug(message: String?, t: Throwable?) {
        logInstance().let { log ->
            log.debug(message, t)
            if (log.isDebugEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Debug.name, message, t))
            }
        }
    }

    override fun warn(message: String?) {
        logInstance().let { log ->
            log.warn(message)
            if (log.isWarnEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Warn.name, message))
            }
        }
    }

    override fun warn(message: String?, vararg variable: Any?) {
        logInstance().let { log ->
            log.warn(message, *variable)
            if (log.isWarnEnabled()) {
                generateLogInfo(AcpCloudLogLevel.Warn.name, message).let {
                    it.params = arrayListOf(*variable)
                    sendToLogServer(it)
                }
            }
        }
    }

    override fun warn(message: String?, t: Throwable?) {
        logInstance().let { log ->
            log.warn(message, t)
            if (log.isWarnEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Warn.name, message, t))
            }
        }
    }

    override fun error(message: String?) {
        logInstance().let { log ->
            log.error(message)
            if (log.isErrorEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Error.name, message))
            }
        }
    }

    override fun error(message: String?, vararg variable: Any?) {
        logInstance().let { log ->
            log.error(message, *variable)
            if (log.isErrorEnabled()) {
                generateLogInfo(AcpCloudLogLevel.Error.name, message).let {
                    it.params = arrayListOf(*variable)
                    sendToLogServer(it)
                }
            }
        }
    }

    override fun error(message: String?, t: Throwable?) {
        logInstance().let { log ->
            log.error(message, t)
            if (log.isErrorEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Error.name, message, t))
            }
        }
    }

    override fun trace(message: String?) {
        logInstance().let { log ->
            log.trace(message)
            if (log.isTraceEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Trace.name, message))
            }
        }
    }

    override fun trace(message: String?, vararg variable: Any?) {
        logInstance().let { log ->
            log.trace(message, *variable)
            if (log.isTraceEnabled()) {
                generateLogInfo(AcpCloudLogLevel.Trace.name, message).let {
                    it.params = arrayListOf(*variable)
                    sendToLogServer(it)
                }
            }
        }
    }

    override fun trace(message: String?, t: Throwable?) {
        logInstance().let { log ->
            log.trace(message, t)
            if (log.isTraceEnabled()) {
                sendToLogServer(generateLogInfo(AcpCloudLogLevel.Trace.name, message, t))
            }
        }
    }

}
